{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:41:29.893914Z",
     "start_time": "2025-01-20T07:41:27.487296Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T13:47:49.178391Z",
     "iopub.status.busy": "2025-01-22T13:47:49.177984Z",
     "iopub.status.idle": "2025-01-22T13:47:51.539784Z",
     "shell.execute_reply": "2025-01-22T13:47:51.538989Z",
     "shell.execute_reply.started": "2025-01-22T13:47:49.178349Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T13:47:53.529506Z",
     "iopub.status.busy": "2025-01-22T13:47:53.528974Z",
     "iopub.status.idle": "2025-01-22T13:47:54.438440Z",
     "shell.execute_reply": "2025-01-22T13:47:54.437741Z",
     "shell.execute_reply.started": "2025-01-22T13:47:53.529478Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "kaggle                            1.6.17\n",
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip list|grep kaggle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T13:48:41.848672Z",
     "iopub.status.busy": "2025-01-22T13:48:41.848289Z",
     "iopub.status.idle": "2025-01-22T13:48:42.555716Z",
     "shell.execute_reply": "2025-01-22T13:48:42.554979Z",
     "shell.execute_reply.started": "2025-01-22T13:48:41.848646Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "- path is now set to: /content\n"
     ]
    }
   ],
   "source": [
    "!mkdir -p ~/.kaggle\n",
    "!cp /mnt/workspace/chapter_6/kaggle.json ~/.kaggle/\n",
    "!chmod 600 ~/.kaggle/kaggle.json\n",
    "!kaggle config set -n path -v /content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:19:08.956938Z",
     "iopub.status.busy": "2025-01-22T14:19:08.956538Z",
     "iopub.status.idle": "2025-01-22T14:20:09.375166Z",
     "shell.execute_reply": "2025-01-22T14:20:09.374412Z",
     "shell.execute_reply.started": "2025-01-22T14:19:08.956908Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading cifar-10.zip to /content/competitions/cifar-10\n",
      "... resuming from 117440512 bytes (632706136 bytes left) ...\n",
      "100%|███████████████████████████████████████▉| 715M/715M [00:50<00:00, 13.3MB/s]\n",
      "100%|████████████████████████████████████████| 715M/715M [00:50<00:00, 12.6MB/s]\n"
     ]
    }
   ],
   "source": [
    "!kaggle competitions download -c cifar-10 #下载比赛数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:20:13.587810Z",
     "iopub.status.busy": "2025-01-22T14:20:13.587408Z",
     "iopub.status.idle": "2025-01-22T14:20:13.704734Z",
     "shell.execute_reply": "2025-01-22T14:20:13.704072Z",
     "shell.execute_reply.started": "2025-01-22T14:20:13.587780Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cifar-10.zip\n"
     ]
    }
   ],
   "source": [
    "!ls /content/competitions/cifar-10/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:20:16.594686Z",
     "iopub.status.busy": "2025-01-22T14:20:16.594298Z",
     "iopub.status.idle": "2025-01-22T14:20:20.403514Z",
     "shell.execute_reply": "2025-01-22T14:20:20.402786Z",
     "shell.execute_reply.started": "2025-01-22T14:20:16.594659Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Archive:  /content/competitions/cifar-10/cifar-10.zip\n",
      "  inflating: sampleSubmission.csv    \n",
      "  inflating: test.7z                 \n",
      "  inflating: train.7z                \n",
      "  inflating: trainLabels.csv         \n"
     ]
    }
   ],
   "source": [
    "!unzip /content/competitions/cifar-10/cifar-10.zip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:20:27.911401Z",
     "iopub.status.busy": "2025-01-22T14:20:27.911015Z",
     "iopub.status.idle": "2025-01-22T14:20:28.028236Z",
     "shell.execute_reply": "2025-01-22T14:20:28.027339Z",
     "shell.execute_reply.started": "2025-01-22T14:20:27.911373Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "01-classification_model-cnn.ipynb\n",
      "02_classification_model-cnn-selu.ipynb\n",
      "03_classification_model-separable_cnn.ipynb\n",
      "04_10_monkeys_model_1_冲突文件_Administrator_20240815203110.ipynb\n",
      "04_10_monkeys_model_1_aliyun.ipynb\n",
      "04_10_monkeys_model_1.ipynb\n",
      "05_10_monkeys_model_2_resnet50_finetune_2aliyun.ipynb\n",
      "05_10_monkeys_model_2_resnet50_finetune_冲突文件_Administrator_20240815203110.ipynb\n",
      "05_10_monkeys_model_2_resnet50_finetune.ipynb\n",
      "06_cifar10_model_1_冲突文件_Administrator_20240815203110.ipynb\n",
      "06_cifar10_model_1.ipynb\n",
      "06_cifar10_model_2-aliyun.ipynb\n",
      "archive\n",
      "checkpoints\n",
      "data\n",
      "kaggle.json\n",
      "runs\n",
      "sampleSubmission.csv\n",
      "test.7z\n",
      "train.7z\n",
      "trainLabels.csv\n"
     ]
    }
   ],
   "source": [
    "!ls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:20:36.048226Z",
     "iopub.status.busy": "2025-01-22T14:20:36.047874Z",
     "iopub.status.idle": "2025-01-22T14:21:00.267857Z",
     "shell.execute_reply": "2025-01-22T14:21:00.267229Z",
     "shell.execute_reply.started": "2025-01-22T14:20:36.048198Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://mirrors.cloud.aliyuncs.com/pypi/simple\n",
      "Collecting py7zr\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/d0/59/dd1750002c0f46099281116f8165247bc62dc85edad41cdd26e7b26de19d/py7zr-0.22.0-py3-none-any.whl (67 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m67.9/67.9 kB\u001b[0m \u001b[31m8.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting texttable (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl (10 kB)\n",
      "Collecting pycryptodomex>=3.16.0 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/46/3f/f5bef92b11750af9e3516d4e69736eeeff20a2818d34611508bef5a7b381/pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.3/2.3 MB\u001b[0m \u001b[31m70.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting pyzstd>=0.15.9 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/6b/3e/e4c7f449af9d19975ff5d333a58330317cf8b05fe4754106c694a29e7c25/pyzstd-0.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (413 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m413.7/413.7 kB\u001b[0m \u001b[31m62.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting pyppmd<1.2.0,>=1.1.0 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/8f/f7/be56704aef53490da6bbe6e2e7efff3c13383310ac71f8a82d15d84bbedb/pyppmd-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (138 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m139.0/139.0 kB\u001b[0m \u001b[31m32.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting pybcj<1.1.0,>=1.0.0 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/d0/6a/3937d0915590d91ccab40b687a1c87429b17325240e8db7e9ad828c9cae9/pybcj-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (49 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m11.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting multivolumefile>=0.2.3 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/22/31/ec5f46fd4c83185b806aa9c736e228cb780f13990a9cf4da0beb70025fcc/multivolumefile-0.2.3-py3-none-any.whl (17 kB)\n",
      "Collecting inflate64<1.1.0,>=1.0.0 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/b3/3f/1af3164e8bda3fdce692c260f01e37c0af21c3022e7227795524505d8b21/inflate64-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (93 kB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m93.3/93.3 kB\u001b[0m \u001b[31m22.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hCollecting brotli>=1.1.0 (from py7zr)\n",
      "  Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (3.0 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.0/3.0 MB\u001b[0m \u001b[31m129.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
      "\u001b[?25hRequirement already satisfied: psutil in /usr/local/lib/python3.10/site-packages (from py7zr) (6.1.1)\n",
      "Installing collected packages: texttable, brotli, pyzstd, pyppmd, pycryptodomex, pybcj, multivolumefile, inflate64, py7zr\n",
      "Successfully installed brotli-1.1.0 inflate64-1.0.1 multivolumefile-0.2.3 py7zr-0.22.0 pybcj-1.0.3 pycryptodomex-3.21.0 pyppmd-1.1.1 pyzstd-0.16.2 texttable-1.7.0\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install py7zr\n",
    "import py7zr\n",
    "a =py7zr.SevenZipFile(r'./train.7z','r')\n",
    "a.extractall(path=r'./competitions/cifar-10/')\n",
    "a.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:21:17.281400Z",
     "iopub.status.busy": "2025-01-22T14:21:17.281029Z",
     "iopub.status.idle": "2025-01-22T14:22:54.831446Z",
     "shell.execute_reply": "2025-01-22T14:22:54.830818Z",
     "shell.execute_reply.started": "2025-01-22T14:21:17.281374Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "a =py7zr.SevenZipFile(r'./test.7z','r')\n",
    "a.extractall(path=r'./competitions/cifar-10/')\n",
    "a.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:23:09.910399Z",
     "iopub.status.busy": "2025-01-22T14:23:09.910024Z",
     "iopub.status.idle": "2025-01-22T14:23:10.123362Z",
     "shell.execute_reply": "2025-01-22T14:23:10.122623Z",
     "shell.execute_reply.started": "2025-01-22T14:23:09.910373Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10/train/ |wc -l  #wc -l统计数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T06:15:58.881339Z",
     "iopub.status.busy": "2025-01-21T06:15:58.880960Z",
     "iopub.status.idle": "2025-01-21T06:15:59.061075Z",
     "shell.execute_reply": "2025-01-21T06:15:59.060470Z",
     "shell.execute_reply.started": "2025-01-21T06:15:58.881312Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50000\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10/train/ |wc -l "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:51:56.103713Z",
     "start_time": "2025-01-20T07:51:54.990355Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:23:16.720484Z",
     "iopub.status.busy": "2025-01-22T14:23:16.720091Z",
     "iopub.status.idle": "2025-01-22T14:23:19.436224Z",
     "shell.execute_reply": "2025-01-22T14:23:19.435583Z",
     "shell.execute_reply.started": "2025-01-22T14:23:16.720454Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder): #filepath:csv文件路径，folder:图片所在文件夹\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标题\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',') #图片id 和标签分离\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T07:52:54.792839Z",
     "start_time": "2025-01-20T07:52:54.757327Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:23:31.200894Z",
     "iopub.status.busy": "2025-01-22T14:23:31.200514Z",
     "iopub.status.idle": "2025-01-22T14:23:31.274227Z",
     "shell.execute_reply": "2025-01-22T14:23:31.273719Z",
     "shell.execute_reply.started": "2025-01-22T14:23:31.200869Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000]) # 取前45000张图片作为训练集\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:]) # 取后5000张图片作为验证集\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:08:23.507372Z",
     "start_time": "2025-01-20T08:08:22.474521Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:23:32.965879Z",
     "iopub.status.busy": "2025-01-22T14:23:32.965506Z",
     "iopub.status.idle": "2025-01-22T14:23:33.837944Z",
     "shell.execute_reply": "2025-01-22T14:23:33.837315Z",
     "shell.execute_reply.started": "2025-01-22T14:23:32.965854Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)} # 类别映射为idx\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)} # idx映射为类别,为了test测试集使用\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None) # 获取对应模式的df，不同字符串对应不同模式\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "        # assert self.df, \"df is None\"\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index] # 获取图片路径和标签\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img) # 数据增强\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0] # 返回df的行数,样本数\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)), #缩放\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40), #随机旋转\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),  #随机水平翻转\n",
    "        transforms.ToTensor(), #转换为tensor\n",
    "        # transforms.Normalize(mean, std) #标准化\n",
    "    ]) #数据增强\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        # transforms.Normalize(mean, std)\n",
    "    ])\n",
    "# ToTensor还将图像的维度从[height, width, channels]转换为[channels, height, width]。\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:24:13.680280Z",
     "iopub.status.busy": "2025-01-22T14:24:13.679904Z",
     "iopub.status.idle": "2025-01-22T14:24:13.685554Z",
     "shell.execute_reply": "2025-01-22T14:24:13.684986Z",
     "shell.execute_reply.started": "2025-01-22T14:24:13.680254Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_ds[0][1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:24:19.328687Z",
     "iopub.status.busy": "2025-01-22T14:24:19.328012Z",
     "iopub.status.idle": "2025-01-22T14:24:19.334198Z",
     "shell.execute_reply": "2025-01-22T14:24:19.333530Z",
     "shell.execute_reply.started": "2025-01-22T14:24:19.328659Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "242.25491"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sample1=train_ds[0][0]\n",
    "np.sum(sample1[2].numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-24T12:26:52.532323800Z",
     "start_time": "2024-07-24T12:26:52.525328700Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-22T14:24:31.993359Z",
     "iopub.status.busy": "2025-01-22T14:24:31.992743Z",
     "iopub.status.idle": "2025-01-22T14:24:31.998172Z",
     "shell.execute_reply": "2025-01-22T14:24:31.997479Z",
     "shell.execute_reply.started": "2025-01-22T14:24:31.993332Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: 'airplane', 1: 'automobile', 2: 'bird', 3: 'cat', 4: 'deer', 5: 'dog', 6: 'frog', 7: 'horse', 8: 'ship', 9: 'truck'}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'airplane': 0,\n",
       " 'automobile': 1,\n",
       " 'bird': 2,\n",
       " 'cat': 3,\n",
       " 'deer': 4,\n",
       " 'dog': 5,\n",
       " 'frog': 6,\n",
       " 'horse': 7,\n",
       " 'ship': 8,\n",
       " 'truck': 9}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(train_ds.idx_to_label)  # 类别映射为idx\n",
    "train_ds.label_to_idx # idx映射为类别"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:08:29.255263Z",
     "start_time": "2025-01-20T08:08:29.252817Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:24:33.621885Z",
     "iopub.status.busy": "2025-01-22T14:24:33.621525Z",
     "iopub.status.idle": "2025-01-22T14:24:33.625701Z",
     "shell.execute_reply": "2025-01-22T14:24:33.624966Z",
     "shell.execute_reply.started": "2025-01-22T14:24:33.621858Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:06.698468Z",
     "iopub.status.busy": "2025-01-22T14:25:06.698092Z",
     "iopub.status.idle": "2025-01-22T14:25:06.823495Z",
     "shell.execute_reply": "2025-01-22T14:25:06.822736Z",
     "shell.execute_reply.started": "2025-01-22T14:25:06.698440Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rw-r--r-- 1 root root 1953 10月 19  2013 /mnt/workspace/chapter_6/competitions/cifar-10/train/17742.png\n"
     ]
    }
   ],
   "source": [
    "!ls -l /mnt/workspace/chapter_6/competitions/cifar-10/train/17742.png"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:14.758197Z",
     "iopub.status.busy": "2025-01-22T14:25:14.757816Z",
     "iopub.status.idle": "2025-01-22T14:25:34.218352Z",
     "shell.execute_reply": "2025-01-22T14:25:34.217718Z",
     "shell.execute_reply.started": "2025-01-22T14:25:14.758168Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([0.4370, 0.4269, 0.3948]), tensor([0.2463, 0.2418, 0.2359]))\n"
     ]
    }
   ],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "# 经过 normalize 后 均值为0，方差为1\n",
    "print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:56.579457400Z",
     "start_time": "2024-07-23T06:49:56.499468400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:40.075568Z",
     "iopub.status.busy": "2025-01-22T14:25:40.075163Z",
     "iopub.status.idle": "2025-01-22T14:25:40.149673Z",
     "shell.execute_reply": "2025-01-22T14:25:40.148940Z",
     "shell.execute_reply.started": "2025-01-22T14:25:40.075541Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 3456\n",
      "              model.0.bias              paramerters num: 128\n",
      "             model.2.weight             paramerters num: 128\n",
      "              model.2.bias              paramerters num: 128\n",
      "             model.3.weight             paramerters num: 147456\n",
      "              model.3.bias              paramerters num: 128\n",
      "             model.5.weight             paramerters num: 128\n",
      "              model.5.bias              paramerters num: 128\n",
      "             model.7.weight             paramerters num: 294912\n",
      "              model.7.bias              paramerters num: 256\n",
      "             model.9.weight             paramerters num: 256\n",
      "              model.9.bias              paramerters num: 256\n",
      "            model.10.weight             paramerters num: 589824\n",
      "             model.10.bias              paramerters num: 256\n",
      "            model.12.weight             paramerters num: 256\n",
      "             model.12.bias              paramerters num: 256\n",
      "            model.14.weight             paramerters num: 1179648\n",
      "             model.14.bias              paramerters num: 512\n",
      "            model.16.weight             paramerters num: 512\n",
      "             model.16.bias              paramerters num: 512\n",
      "            model.17.weight             paramerters num: 2359296\n",
      "             model.17.bias              paramerters num: 512\n",
      "            model.19.weight             paramerters num: 512\n",
      "             model.19.bias              paramerters num: 512\n",
      "            model.22.weight             paramerters num: 4194304\n",
      "             model.22.bias              paramerters num: 512\n",
      "            model.24.weight             paramerters num: 5120\n",
      "             model.24.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class CNN(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=128, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(128),# 批标准化，在通道数做的归一化\n",
    "            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=\"same\"), #输出尺寸（128，32，32）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.MaxPool2d(kernel_size=2), #输出尺寸（128，16，16）\n",
    "            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(256),\n",
    "            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, padding=\"same\"), #输出尺寸（256，16，16）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(256),\n",
    "            nn.MaxPool2d(kernel_size=2),#输出尺寸（256，8，8）\n",
    "            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, padding=\"same\"), #输出尺寸（512，8，8）\n",
    "            nn.ReLU(),\n",
    "            nn.BatchNorm2d(512),\n",
    "            nn.MaxPool2d(kernel_size=2), #输出尺寸（512，4，4）\n",
    "            nn.Flatten(), #展平\n",
    "            nn.Linear(8192, 512),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(512, num_classes),\n",
    "        ) #Sequential自动连接各层，把各层的输出作为下一层的输入\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "for key, value in CNN(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:59.043655400Z",
     "start_time": "2024-07-23T06:49:58.976307900Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:43.947268Z",
     "iopub.status.busy": "2025-01-22T14:25:43.946904Z",
     "iopub.status.idle": "2025-01-22T14:25:44.009987Z",
     "shell.execute_reply": "2025-01-22T14:25:44.009365Z",
     "shell.execute_reply.started": "2025-01-22T14:25:43.947242Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total trainable parameters: 8779914\n"
     ]
    }
   ],
   "source": [
    "total_params = sum(p.numel() for p in CNN(len(class_names)).parameters() if p.requires_grad)\n",
    "print(f\"Total trainable parameters: {total_params}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:49:14.250276700Z",
     "start_time": "2024-07-23T06:49:14.242278700Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:45.930962Z",
     "iopub.status.busy": "2025-01-22T14:25:45.930594Z",
     "iopub.status.idle": "2025-01-22T14:25:45.935988Z",
     "shell.execute_reply": "2025-01-22T14:25:45.935197Z",
     "shell.execute_reply.started": "2025-01-22T14:25:45.930938Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8192"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "512*4*4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:30.878296Z",
     "start_time": "2025-01-20T08:18:30.872291Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:47.050443Z",
     "iopub.status.busy": "2025-01-22T14:25:47.050082Z",
     "iopub.status.idle": "2025-01-22T14:25:47.059048Z",
     "shell.execute_reply": "2025-01-22T14:25:47.058200Z",
     "shell.execute_reply.started": "2025-01-22T14:25:47.050404Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([32, 3, 64, 64])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input_4d = torch.randn(32, 3, 64, 64)  # 32 个样本，3 个通道，图像大小为 64x64\n",
    "bn2d = nn.BatchNorm2d(3)              # 对 3 个通道进行归一化\n",
    "output_4d = bn2d(input_4d)\n",
    "output_4d.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:32.896417Z",
     "start_time": "2025-01-20T08:18:32.888443Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:48.211351Z",
     "iopub.status.busy": "2025-01-22T14:25:48.211012Z",
     "iopub.status.idle": "2025-01-22T14:25:48.224910Z",
     "shell.execute_reply": "2025-01-22T14:25:48.224206Z",
     "shell.execute_reply.started": "2025-01-22T14:25:48.211326Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[ 1.4397e-01,  5.1619e-01, -1.2484e-01,  ..., -1.3814e+00,\n",
       "            1.3254e+00, -1.5735e-01],\n",
       "          [-1.1292e+00, -8.3262e-01, -2.7503e-01,  ..., -1.7367e-01,\n",
       "           -1.6710e+00,  2.4455e-01],\n",
       "          [ 7.5156e-01,  3.9777e-01, -3.5705e-02,  ...,  1.2039e+00,\n",
       "           -1.0034e+00,  3.3042e-01],\n",
       "          ...,\n",
       "          [ 1.7877e-01,  7.4147e-02, -1.8184e+00,  ...,  6.6619e-01,\n",
       "           -4.0016e-01, -3.8365e-01],\n",
       "          [ 1.0544e-01,  2.3650e+00, -6.5760e-01,  ...,  7.6373e-01,\n",
       "            5.9914e-01, -7.0275e-01],\n",
       "          [ 6.0445e-01, -7.3661e-01,  3.6579e-01,  ..., -1.7625e+00,\n",
       "            2.9014e-01,  2.0990e+00]],\n",
       "\n",
       "         [[-4.4619e-01,  4.2026e-01,  2.7460e-01,  ..., -8.7921e-01,\n",
       "           -5.0393e-01, -4.1494e-02],\n",
       "          [ 1.7477e+00, -7.7628e-01,  2.0066e+00,  ..., -4.7988e-01,\n",
       "            1.3836e+00, -1.0883e-01],\n",
       "          [ 6.7471e-01,  2.2182e-01,  4.2866e-01,  ...,  1.7045e-01,\n",
       "           -1.0955e+00,  9.5552e-01],\n",
       "          ...,\n",
       "          [ 4.2335e-01,  7.9771e-01,  1.1552e+00,  ..., -8.1677e-02,\n",
       "            3.7573e-01, -1.8922e+00],\n",
       "          [ 2.4040e+00, -1.1242e-01, -9.6125e-01,  ..., -1.3523e+00,\n",
       "           -3.0519e-01,  1.3067e+00],\n",
       "          [-1.0552e+00, -1.6891e-01,  2.4213e-01,  ..., -2.8635e-02,\n",
       "           -1.3899e-01,  1.8904e+00]],\n",
       "\n",
       "         [[ 4.1983e-02,  3.9569e-01, -4.7603e-01,  ...,  4.3033e-01,\n",
       "           -1.1681e+00,  8.6326e-01],\n",
       "          [-8.5550e-01, -2.1868e-01, -4.4454e-02,  ..., -2.6875e-01,\n",
       "            3.6470e-01, -8.1721e-02],\n",
       "          [-1.9419e+00,  1.9121e-01, -2.2082e-01,  ..., -1.0004e-01,\n",
       "            5.5484e-01,  1.0672e+00],\n",
       "          ...,\n",
       "          [ 1.6079e-03, -3.3912e-01, -1.5363e+00,  ..., -9.6129e-01,\n",
       "            1.2189e+00, -1.2258e+00],\n",
       "          [ 6.3332e-01, -2.8311e-01,  2.6219e-01,  ..., -1.7671e+00,\n",
       "            3.3492e-01, -5.4394e-02],\n",
       "          [-5.4917e-01,  4.8854e-01,  1.1875e-01,  ...,  1.3389e+00,\n",
       "            8.9226e-01, -6.8248e-01]]],\n",
       "\n",
       "\n",
       "        [[[ 7.9633e-02, -4.0154e-01,  7.0959e-01,  ...,  2.0118e-01,\n",
       "            4.9822e-02,  6.7678e-01],\n",
       "          [-6.6904e-01, -4.1784e-02,  5.5442e-01,  ..., -1.5804e+00,\n",
       "            5.6361e-01, -1.4426e+00],\n",
       "          [ 5.5719e-02,  5.4848e-01, -3.2595e-01,  ...,  3.9759e-01,\n",
       "           -2.4282e-01, -1.9286e-01],\n",
       "          ...,\n",
       "          [ 1.0690e+00,  1.0595e+00,  3.9719e-01,  ...,  6.2192e-01,\n",
       "            4.8750e-01,  2.2924e-02],\n",
       "          [-1.1172e-03,  1.3138e+00,  1.5491e+00,  ...,  1.7993e-01,\n",
       "            6.3357e-01,  1.3431e+00],\n",
       "          [-5.9694e-01,  1.0193e+00, -2.5221e-01,  ...,  6.1252e-02,\n",
       "           -3.9683e-01, -5.6752e-01]],\n",
       "\n",
       "         [[-6.1101e-01, -3.1114e-01, -2.2316e+00,  ..., -1.1805e+00,\n",
       "            7.7106e-03,  1.2134e+00],\n",
       "          [ 7.8367e-01,  3.1208e-01,  1.3188e-01,  ...,  1.0466e+00,\n",
       "            8.6405e-01, -1.1571e+00],\n",
       "          [ 1.2875e+00,  1.6208e-01, -1.9959e+00,  ..., -3.7249e+00,\n",
       "            6.7407e-01,  6.7038e-01],\n",
       "          ...,\n",
       "          [-6.6653e-01,  9.6828e-01,  1.0706e+00,  ...,  2.1431e-01,\n",
       "           -1.8264e+00, -1.0999e+00],\n",
       "          [-1.2277e+00,  9.1114e-01, -8.2524e-01,  ..., -1.7129e+00,\n",
       "            7.4297e-01, -5.1501e-01],\n",
       "          [ 1.2903e-01,  1.5590e+00, -9.7355e-01,  ...,  1.0437e+00,\n",
       "           -1.4086e+00,  8.5811e-01]],\n",
       "\n",
       "         [[-5.4944e-01, -2.0421e-01, -1.3244e+00,  ...,  3.4577e-01,\n",
       "           -2.9201e-01, -4.7141e-01],\n",
       "          [ 1.6756e-01, -9.5852e-01, -1.4466e+00,  ...,  4.3113e-01,\n",
       "           -1.0277e+00, -5.1888e-01],\n",
       "          [ 2.9825e-01, -4.6714e-01, -4.1971e-01,  ..., -1.1857e+00,\n",
       "            8.6444e-01, -6.0614e-01],\n",
       "          ...,\n",
       "          [ 1.3304e+00, -1.3150e+00,  7.3837e-01,  ..., -1.1364e+00,\n",
       "           -2.4166e-01, -8.9402e-01],\n",
       "          [-1.0610e+00,  1.2819e+00,  1.8418e+00,  ...,  1.2616e+00,\n",
       "           -6.1243e-01, -5.2196e-01],\n",
       "          [ 1.2979e+00, -1.7414e+00,  1.0686e+00,  ...,  1.1697e+00,\n",
       "            2.6225e-01, -3.5438e-01]]],\n",
       "\n",
       "\n",
       "        [[[-4.1133e-01,  6.6672e-01,  4.1254e-01,  ..., -7.0786e-02,\n",
       "           -2.1038e+00,  1.5093e+00],\n",
       "          [ 5.1064e-01,  1.3922e+00, -2.3598e-01,  ...,  6.1544e-01,\n",
       "           -9.7903e-02, -6.4790e-02],\n",
       "          [-2.0211e+00, -7.6079e-01,  5.1705e-01,  ..., -1.7523e+00,\n",
       "            1.0826e+00, -5.9593e-01],\n",
       "          ...,\n",
       "          [ 6.6794e-01,  2.1461e-02, -2.6937e-01,  ...,  1.7830e-01,\n",
       "            8.7996e-01,  1.9955e-01],\n",
       "          [-7.8240e-01, -1.5097e+00, -1.7572e+00,  ..., -1.1543e+00,\n",
       "           -1.8525e-01, -3.9824e-01],\n",
       "          [ 1.1320e+00, -1.2648e-01,  1.4491e-01,  ..., -3.0015e-01,\n",
       "           -1.4786e+00,  8.5506e-02]],\n",
       "\n",
       "         [[ 1.5756e+00, -9.8438e-02,  9.4090e-01,  ...,  5.7002e-01,\n",
       "           -1.4710e+00,  1.2918e+00],\n",
       "          [ 1.4255e-01, -1.9471e-01, -5.2812e-01,  ..., -1.0496e+00,\n",
       "            3.5071e-01, -8.5434e-01],\n",
       "          [ 1.5629e+00, -1.2096e+00, -7.0528e-01,  ..., -1.3141e+00,\n",
       "           -1.5338e-01,  6.8954e-01],\n",
       "          ...,\n",
       "          [-1.5629e+00, -1.2218e+00, -4.8004e-01,  ...,  7.1840e-01,\n",
       "           -1.1369e+00,  2.0783e-01],\n",
       "          [-2.4466e+00, -4.5933e-01,  1.2911e-01,  ...,  9.7966e-01,\n",
       "            1.2305e+00, -3.7467e-02],\n",
       "          [ 1.0408e+00,  1.2223e+00, -1.1716e+00,  ..., -1.8210e+00,\n",
       "           -8.4799e-01, -8.1359e-01]],\n",
       "\n",
       "         [[ 1.7577e+00, -2.7084e+00,  2.9758e-03,  ...,  9.2629e-01,\n",
       "           -1.2965e+00,  1.1515e+00],\n",
       "          [-1.0902e+00, -3.2668e-01,  3.0222e-01,  ...,  1.6061e+00,\n",
       "           -8.1434e-02, -4.1067e-01],\n",
       "          [-2.2988e+00, -3.7048e-01, -1.0778e+00,  ..., -4.1900e-01,\n",
       "            7.6093e-01,  8.2245e-01],\n",
       "          ...,\n",
       "          [ 9.5453e-01,  1.6523e+00,  1.2126e+00,  ..., -1.2187e+00,\n",
       "            2.7171e-01, -4.8406e-01],\n",
       "          [-1.1361e+00, -9.1675e-01, -7.5700e-01,  ..., -7.3415e-01,\n",
       "           -2.2807e-02, -1.5137e+00],\n",
       "          [ 1.3314e+00, -7.4630e-01, -2.2352e-01,  ..., -4.3205e-01,\n",
       "           -1.3165e+00, -8.4357e-01]]],\n",
       "\n",
       "\n",
       "        ...,\n",
       "\n",
       "\n",
       "        [[[-5.2732e-01,  6.9068e-02,  2.7221e-01,  ..., -3.7743e-01,\n",
       "            4.5416e-01, -1.2523e+00],\n",
       "          [ 8.5049e-01,  1.2804e+00,  2.1816e+00,  ..., -9.0925e-01,\n",
       "           -3.1362e-01, -7.4926e-01],\n",
       "          [ 3.5010e-01,  3.0398e-01,  1.2421e-01,  ...,  4.1883e-02,\n",
       "            1.5321e+00,  1.1056e+00],\n",
       "          ...,\n",
       "          [-1.4315e+00, -4.3938e-01,  1.7370e+00,  ...,  9.2348e-02,\n",
       "           -2.3048e-01,  1.7722e+00],\n",
       "          [ 1.0822e+00, -4.0344e-01,  1.5797e+00,  ...,  1.1354e+00,\n",
       "            1.5961e+00,  8.2099e-01],\n",
       "          [-6.0111e-01, -3.1707e-01, -5.8342e-02,  ..., -2.0466e-01,\n",
       "            1.5512e+00,  5.3756e-01]],\n",
       "\n",
       "         [[-8.6925e-01, -8.8729e-01,  7.2297e-01,  ..., -2.3793e-01,\n",
       "            7.5758e-01, -3.8157e-01],\n",
       "          [ 6.9378e-01, -1.5641e+00,  9.8072e-01,  ..., -1.5932e+00,\n",
       "           -7.9604e-01,  9.2259e-01],\n",
       "          [ 1.3492e+00, -1.6214e+00,  2.1658e+00,  ...,  1.2022e-01,\n",
       "           -1.6812e+00,  1.8824e-01],\n",
       "          ...,\n",
       "          [ 5.9034e-01, -3.4949e-01, -1.1640e+00,  ..., -3.2423e-01,\n",
       "           -3.6239e-01, -1.0358e+00],\n",
       "          [-2.2748e-02, -3.0075e-01, -2.6301e-01,  ...,  1.6861e+00,\n",
       "           -1.5813e+00,  2.2174e-01],\n",
       "          [-6.5159e-01, -5.5755e-01, -1.2663e+00,  ...,  6.5098e-01,\n",
       "            6.6553e-01, -8.7100e-01]],\n",
       "\n",
       "         [[ 2.1123e-01, -1.6687e+00, -3.5991e-01,  ...,  1.2473e+00,\n",
       "           -3.3546e-01, -3.3541e-01],\n",
       "          [-9.7653e-01,  5.1445e-01, -7.2124e-01,  ...,  1.8625e+00,\n",
       "           -5.0574e-01,  1.3472e+00],\n",
       "          [-6.5579e-01, -1.7958e+00, -4.5768e-01,  ...,  7.9860e-01,\n",
       "            4.2562e-01, -3.8490e-01],\n",
       "          ...,\n",
       "          [ 1.0969e-01, -2.8424e-02,  5.9701e-02,  ..., -3.6757e-01,\n",
       "            2.5626e-01,  2.3478e+00],\n",
       "          [ 1.7299e+00,  1.4102e+00,  1.8734e+00,  ...,  7.1787e-01,\n",
       "            8.1650e-01,  6.1412e-01],\n",
       "          [ 9.1226e-01, -4.1333e-01, -7.1994e-01,  ...,  3.5849e-01,\n",
       "            3.8680e-01, -3.7618e-01]]],\n",
       "\n",
       "\n",
       "        [[[-5.0306e-01,  2.7719e-01,  8.5852e-01,  ..., -1.2319e+00,\n",
       "           -1.1177e+00,  2.1767e-01],\n",
       "          [-1.2399e-01,  2.8995e+00,  1.0489e+00,  ...,  1.2274e+00,\n",
       "           -1.0554e+00, -7.0475e-01],\n",
       "          [ 6.9536e-01,  1.7742e+00,  7.3529e-01,  ...,  7.0042e-01,\n",
       "           -1.7779e+00, -1.2020e+00],\n",
       "          ...,\n",
       "          [-1.0697e+00, -4.6273e-01,  7.8724e-01,  ..., -8.8981e-01,\n",
       "            7.0796e-01,  7.5855e-01],\n",
       "          [ 4.1308e-02, -9.2841e-01,  4.0022e-01,  ..., -1.5592e+00,\n",
       "            1.6932e+00, -1.2244e+00],\n",
       "          [-9.5556e-01, -1.3098e+00,  4.0531e-01,  ...,  1.1344e+00,\n",
       "            2.8641e-01, -2.5188e+00]],\n",
       "\n",
       "         [[-1.4353e+00,  1.5858e+00,  2.2099e-01,  ..., -9.9510e-01,\n",
       "            8.7303e-01, -1.3262e+00],\n",
       "          [-1.5284e+00, -3.4497e-01, -3.3744e-01,  ..., -2.9303e-01,\n",
       "            3.4938e-01, -1.3738e+00],\n",
       "          [ 1.4895e+00,  1.1816e+00, -1.6269e-01,  ..., -1.3729e+00,\n",
       "            1.3682e+00, -9.3737e-03],\n",
       "          ...,\n",
       "          [-1.6564e+00,  4.5559e-01, -7.9832e-02,  ..., -1.2845e-01,\n",
       "           -1.1158e-02, -1.3269e+00],\n",
       "          [ 1.8373e+00, -1.6329e+00,  4.1669e-01,  ...,  2.4535e+00,\n",
       "            6.8303e-01,  5.3439e-01],\n",
       "          [ 3.3197e-01, -2.5319e-01, -2.2888e-01,  ...,  2.7226e+00,\n",
       "           -1.6954e+00, -2.1124e-01]],\n",
       "\n",
       "         [[ 5.9424e-01,  3.5672e-01,  1.4772e+00,  ..., -4.4002e-01,\n",
       "            2.9615e-01,  1.0691e+00],\n",
       "          [ 1.5063e+00, -1.3194e-01, -1.9224e-01,  ...,  6.7245e-01,\n",
       "           -1.7565e+00,  9.2091e-01],\n",
       "          [-3.4808e-01, -1.8508e+00, -6.8182e-01,  ...,  1.5568e-01,\n",
       "            1.7628e-01,  1.2837e-01],\n",
       "          ...,\n",
       "          [ 1.1926e+00, -1.4331e-01, -1.1397e+00,  ..., -1.3196e+00,\n",
       "            1.4774e+00, -1.1204e+00],\n",
       "          [ 6.3008e-01, -9.5589e-01,  2.9117e+00,  ...,  6.9630e-01,\n",
       "            7.3643e-01,  2.3822e+00],\n",
       "          [-8.2558e-01,  4.8902e-01,  1.0052e+00,  ..., -8.7128e-02,\n",
       "            1.4238e+00,  1.5745e-01]]],\n",
       "\n",
       "\n",
       "        [[[-1.8192e+00,  9.1008e-02, -8.5075e-01,  ...,  2.0474e-01,\n",
       "            1.8107e-01,  6.3862e-01],\n",
       "          [-4.9755e-01, -6.4180e-01,  7.0662e-01,  ...,  3.2917e-01,\n",
       "           -2.1366e-01, -4.7629e-01],\n",
       "          [-4.2929e-01,  6.5745e-01, -1.4109e+00,  ...,  1.3411e+00,\n",
       "            2.4146e-01, -1.1393e+00],\n",
       "          ...,\n",
       "          [ 8.4116e-01,  1.3829e+00,  3.9487e-01,  ...,  1.3881e+00,\n",
       "            4.2991e-01, -2.4781e-01],\n",
       "          [-1.7887e+00, -1.0282e+00, -8.3443e-01,  ..., -1.0970e+00,\n",
       "           -2.9981e-01,  2.8294e-01],\n",
       "          [ 1.3165e+00, -6.5922e-01, -4.3937e-01,  ...,  6.9725e-01,\n",
       "            4.4482e-02, -1.8404e+00]],\n",
       "\n",
       "         [[-6.2927e-03, -8.3650e-01, -9.9237e-01,  ..., -7.4127e-01,\n",
       "           -1.3843e+00,  2.5368e-01],\n",
       "          [ 6.1233e-01,  1.6550e-01, -5.0101e-01,  ..., -1.2722e+00,\n",
       "            8.7942e-02,  7.7893e-02],\n",
       "          [ 5.1358e-01, -1.0458e+00,  1.1329e+00,  ...,  1.3057e+00,\n",
       "            3.4258e-01,  7.4500e-02],\n",
       "          ...,\n",
       "          [-2.2219e+00,  3.5904e-01, -5.7440e-02,  ..., -3.9093e-01,\n",
       "            2.0540e+00,  1.5250e-01],\n",
       "          [-5.3170e-01, -2.0993e-04, -1.2877e-01,  ..., -1.9861e+00,\n",
       "            6.8647e-02, -9.7559e-01],\n",
       "          [ 5.9728e-01,  1.2057e+00,  4.3050e-01,  ...,  6.3073e-01,\n",
       "           -3.2529e-01, -1.7427e-01]],\n",
       "\n",
       "         [[ 5.8427e-01, -8.9272e-01,  4.8567e-01,  ..., -1.1573e+00,\n",
       "            4.1999e-01, -2.5155e+00],\n",
       "          [-2.9525e-01, -5.8896e-01, -8.9996e-01,  ...,  1.9361e-01,\n",
       "            8.3733e-02,  1.2429e+00],\n",
       "          [-1.4492e+00, -9.2108e-01, -7.2071e-01,  ..., -7.0069e-01,\n",
       "           -4.2008e-01,  1.2803e+00],\n",
       "          ...,\n",
       "          [-4.8340e-01,  2.3617e-01, -1.6722e+00,  ...,  1.1865e+00,\n",
       "            1.4910e-01, -4.8419e-01],\n",
       "          [ 1.1608e+00,  1.3707e+00, -4.9654e-01,  ..., -3.0031e-01,\n",
       "            8.9201e-01,  6.1512e-01],\n",
       "          [ 1.2724e+00,  1.0000e+00,  7.4186e-01,  ...,  1.3964e-01,\n",
       "           -3.8862e-01,  8.9315e-01]]]], grad_fn=<NativeBatchNormBackward0>)"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_4d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-20T08:18:50.605471Z",
     "start_time": "2025-01-20T08:18:50.598876Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:52.617903Z",
     "iopub.status.busy": "2025-01-22T14:25:52.617565Z",
     "iopub.status.idle": "2025-01-22T14:25:52.627917Z",
     "shell.execute_reply": "2025-01-22T14:25:52.627320Z",
     "shell.execute_reply.started": "2025-01-22T14:25:52.617876Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[[[ 1.4397e-01,  5.1619e-01, -1.2484e-01,  ..., -1.3814e+00,\n",
       "            1.3254e+00, -1.5735e-01],\n",
       "          [-1.1292e+00, -8.3262e-01, -2.7503e-01,  ..., -1.7367e-01,\n",
       "           -1.6710e+00,  2.4455e-01],\n",
       "          [ 7.5156e-01,  3.9777e-01, -3.5705e-02,  ...,  1.2039e+00,\n",
       "           -1.0034e+00,  3.3042e-01],\n",
       "          ...,\n",
       "          [ 1.7877e-01,  7.4147e-02, -1.8184e+00,  ...,  6.6619e-01,\n",
       "           -4.0016e-01, -3.8365e-01],\n",
       "          [ 1.0544e-01,  2.3650e+00, -6.5760e-01,  ...,  7.6373e-01,\n",
       "            5.9914e-01, -7.0275e-01],\n",
       "          [ 6.0445e-01, -7.3661e-01,  3.6579e-01,  ..., -1.7625e+00,\n",
       "            2.9014e-01,  2.0990e+00]]],\n",
       "\n",
       "\n",
       "        [[[ 7.9633e-02, -4.0154e-01,  7.0959e-01,  ...,  2.0118e-01,\n",
       "            4.9822e-02,  6.7678e-01],\n",
       "          [-6.6904e-01, -4.1784e-02,  5.5442e-01,  ..., -1.5804e+00,\n",
       "            5.6361e-01, -1.4426e+00],\n",
       "          [ 5.5719e-02,  5.4848e-01, -3.2595e-01,  ...,  3.9759e-01,\n",
       "           -2.4282e-01, -1.9286e-01],\n",
       "          ...,\n",
       "          [ 1.0690e+00,  1.0595e+00,  3.9719e-01,  ...,  6.2192e-01,\n",
       "            4.8750e-01,  2.2924e-02],\n",
       "          [-1.1172e-03,  1.3138e+00,  1.5491e+00,  ...,  1.7993e-01,\n",
       "            6.3357e-01,  1.3431e+00],\n",
       "          [-5.9694e-01,  1.0193e+00, -2.5221e-01,  ...,  6.1252e-02,\n",
       "           -3.9683e-01, -5.6752e-01]]],\n",
       "\n",
       "\n",
       "        [[[-4.1133e-01,  6.6672e-01,  4.1254e-01,  ..., -7.0786e-02,\n",
       "           -2.1038e+00,  1.5093e+00],\n",
       "          [ 5.1064e-01,  1.3922e+00, -2.3598e-01,  ...,  6.1544e-01,\n",
       "           -9.7903e-02, -6.4790e-02],\n",
       "          [-2.0211e+00, -7.6079e-01,  5.1705e-01,  ..., -1.7523e+00,\n",
       "            1.0826e+00, -5.9593e-01],\n",
       "          ...,\n",
       "          [ 6.6794e-01,  2.1461e-02, -2.6937e-01,  ...,  1.7830e-01,\n",
       "            8.7996e-01,  1.9955e-01],\n",
       "          [-7.8240e-01, -1.5097e+00, -1.7572e+00,  ..., -1.1543e+00,\n",
       "           -1.8525e-01, -3.9824e-01],\n",
       "          [ 1.1320e+00, -1.2648e-01,  1.4491e-01,  ..., -3.0015e-01,\n",
       "           -1.4786e+00,  8.5506e-02]]],\n",
       "\n",
       "\n",
       "        ...,\n",
       "\n",
       "\n",
       "        [[[-5.2732e-01,  6.9068e-02,  2.7221e-01,  ..., -3.7743e-01,\n",
       "            4.5416e-01, -1.2523e+00],\n",
       "          [ 8.5049e-01,  1.2804e+00,  2.1816e+00,  ..., -9.0925e-01,\n",
       "           -3.1362e-01, -7.4926e-01],\n",
       "          [ 3.5010e-01,  3.0398e-01,  1.2421e-01,  ...,  4.1883e-02,\n",
       "            1.5321e+00,  1.1056e+00],\n",
       "          ...,\n",
       "          [-1.4315e+00, -4.3938e-01,  1.7370e+00,  ...,  9.2348e-02,\n",
       "           -2.3048e-01,  1.7722e+00],\n",
       "          [ 1.0822e+00, -4.0344e-01,  1.5797e+00,  ...,  1.1354e+00,\n",
       "            1.5961e+00,  8.2099e-01],\n",
       "          [-6.0111e-01, -3.1707e-01, -5.8342e-02,  ..., -2.0466e-01,\n",
       "            1.5512e+00,  5.3756e-01]]],\n",
       "\n",
       "\n",
       "        [[[-5.0306e-01,  2.7719e-01,  8.5852e-01,  ..., -1.2319e+00,\n",
       "           -1.1177e+00,  2.1767e-01],\n",
       "          [-1.2399e-01,  2.8995e+00,  1.0489e+00,  ...,  1.2274e+00,\n",
       "           -1.0554e+00, -7.0475e-01],\n",
       "          [ 6.9536e-01,  1.7742e+00,  7.3529e-01,  ...,  7.0042e-01,\n",
       "           -1.7779e+00, -1.2020e+00],\n",
       "          ...,\n",
       "          [-1.0697e+00, -4.6273e-01,  7.8724e-01,  ..., -8.8981e-01,\n",
       "            7.0796e-01,  7.5855e-01],\n",
       "          [ 4.1308e-02, -9.2841e-01,  4.0022e-01,  ..., -1.5592e+00,\n",
       "            1.6932e+00, -1.2244e+00],\n",
       "          [-9.5556e-01, -1.3098e+00,  4.0531e-01,  ...,  1.1344e+00,\n",
       "            2.8641e-01, -2.5188e+00]]],\n",
       "\n",
       "\n",
       "        [[[-1.8192e+00,  9.1008e-02, -8.5075e-01,  ...,  2.0474e-01,\n",
       "            1.8107e-01,  6.3862e-01],\n",
       "          [-4.9755e-01, -6.4180e-01,  7.0662e-01,  ...,  3.2917e-01,\n",
       "           -2.1366e-01, -4.7629e-01],\n",
       "          [-4.2929e-01,  6.5745e-01, -1.4109e+00,  ...,  1.3411e+00,\n",
       "            2.4146e-01, -1.1393e+00],\n",
       "          ...,\n",
       "          [ 8.4116e-01,  1.3829e+00,  3.9487e-01,  ...,  1.3881e+00,\n",
       "            4.2991e-01, -2.4781e-01],\n",
       "          [-1.7887e+00, -1.0282e+00, -8.3443e-01,  ..., -1.0970e+00,\n",
       "           -2.9981e-01,  2.8294e-01],\n",
       "          [ 1.3165e+00, -6.5922e-01, -4.3937e-01,  ...,  6.9725e-01,\n",
       "            4.4482e-02, -1.8404e+00]]]], grad_fn=<NativeBatchNormBackward0>)"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bn2d = nn.BatchNorm2d(1)              # 对 3 个通道进行归一化\n",
    "output_4d1 = bn2d(input_4d[:, 0:1, :, :])\n",
    "output_4d1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:47.038660500Z",
     "start_time": "2024-07-23T06:51:46.193237300Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:54.726218Z",
     "iopub.status.busy": "2025-01-22T14:25:54.725852Z",
     "iopub.status.idle": "2025-01-22T14:25:54.758257Z",
     "shell.execute_reply": "2025-01-22T14:25:54.757595Z",
     "shell.execute_reply.started": "2025-01-22T14:25:54.726191Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:50.921239100Z",
     "start_time": "2024-07-23T06:51:49.522951600Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:57.301265Z",
     "iopub.status.busy": "2025-01-22T14:25:57.300794Z",
     "iopub.status.idle": "2025-01-22T14:25:57.370890Z",
     "shell.execute_reply": "2025-01-22T14:25:57.370290Z",
     "shell.execute_reply.started": "2025-01-22T14:25:57.301236Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:52.029341200Z",
     "start_time": "2024-07-23T06:51:52.023343500Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:25:59.206249Z",
     "iopub.status.busy": "2025-01-22T14:25:59.205716Z",
     "iopub.status.idle": "2025-01-22T14:25:59.212176Z",
     "shell.execute_reply": "2025-01-22T14:25:59.211597Z",
     "shell.execute_reply.started": "2025-01-22T14:25:59.206220Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-23T06:51:56.837899700Z",
     "start_time": "2024-07-23T06:51:56.828651400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:26:01.448092Z",
     "iopub.status.busy": "2025-01-22T14:26:01.447711Z",
     "iopub.status.idle": "2025-01-22T14:26:01.452914Z",
     "shell.execute_reply": "2025-01-22T14:26:01.452337Z",
     "shell.execute_reply.started": "2025-01-22T14:26:01.448067Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:26:02.825606Z",
     "iopub.status.busy": "2025-01-22T14:26:02.825146Z",
     "iopub.status.idle": "2025-01-22T14:26:02.969417Z",
     "shell.execute_reply": "2025-01-22T14:26:02.968824Z",
     "shell.execute_reply.started": "2025-01-22T14:26:02.825579Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1) #最大值的索引\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())     # 计算准确率\n",
    "                loss = loss.cpu().item() # 计算损失\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step # 记录每一步的损失和准确率\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = CNN(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "# tensorboard_callback = TensorBoardCallback(\"runs/cifar-10\")\n",
    "# tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/cifar-10\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-22T14:26:39.083593Z",
     "iopub.status.busy": "2025-01-22T14:26:39.083203Z",
     "iopub.status.idle": "2025-01-22T14:37:10.824486Z",
     "shell.execute_reply": "2025-01-22T14:37:10.823896Z",
     "shell.execute_reply.started": "2025-01-22T14:26:39.083562Z"
    },
    "jupyter": {
     "outputs_hidden": false
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 14080/14080 [10:31<00:00, 22.29it/s, epoch=19]\n"
     ]
    }
   ],
   "source": [
    "record = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    eval_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:37:58.205372Z",
     "iopub.status.busy": "2025-01-22T14:37:58.205048Z",
     "iopub.status.idle": "2025-01-22T14:37:58.457184Z",
     "shell.execute_reply": "2025-01-22T14:37:58.456598Z",
     "shell.execute_reply.started": "2025-01-22T14:37:58.205348Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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4sWL3Y779ddfadu2LYWFhc5tffv2pW/fvowfP55WrVoxa9YsRo0aBcAZZ5zBGWecwfjx4+nevTufffYZY8eO9bq22WymuLiYxYsXY7VafcZVkxUUFLB9+3bn4127drFu3Trq169PixYtmDhxIgcOHOCjjz4C4I477uD111/nwQcf5Oabb2bhwoV88cUXzJo1q7peghBChExyn/CEnfyMHTuWv//+m6VLlwbcr1+/fm6fpvXv359OnTrx3//+l6lTp/o8JpJjq//anwsbVgAwePBgkhO9hy3URLE6/jQW447FmKF64y4pKWHfvn3UqVOHxMREzcepqkp+fj6pqalVOvfPVdu2bVm3bh0nTpxwiz81NdXt70uHDh2YPXs2l112GQCPPvooqqpiMpmc++l0OhITE92OS0pKcnusKIrXPr44PthxxPHQQw9x+umn8+qrr3LllVeyfPly3n33XV5//XXS0tL46aef2LVrF2eddRb16tVj9uzZ2O12evbsyebNm1mwYAEDBgygVatWrFy5kmPHjtGzZ0+fcZSUlJCUlMTZZ5/t9fOszLjqqrJ69WoGDRrkfOx4/7jxxhuZMWMGhw4dchuu2Lp1a2bNmsW9997LK6+8QrNmzXj33XelzbUQQrMThWZu/2g1l/dpxtV9/TfBiSap/IQnrORn3Lhx/PTTTyxevNhv9cYfo9FIr1693D6l8xTJsdU6lyEcBkOsjeWMxfGnZWIx7liMGaonbpvNhqIo6HS6kObuOIZtOY6tDg888AA33ngjXbt2pbi4mA8++ADA67W89NJL3HzzzZx55plkZGRw1113UVxc7BW752Nf3xMt3yfH8459Tz31VL744gsmT57MU089RePGjXnyySe5+eabgbLhxtOmTWPKlCmUlJRwyimn8Omnn9KtWzc2b97MkiVLeOWVV8jPz6dly5a8+OKLjBw50u+1FUXx+bsUC/8mBg4cGHDo44wZM3wes3bt2ihGJYSozabN38rqPSdZveekJD8xJqTkR1VV7rrrLr799lsWLVrkc+x4MDabjQ0bNlRZVwq7y++F/IoIIdq3b8/y5cvdto0ZM8Zrv1atWrFw4UKgLGnLy8vjvvvuc0tidu/e7XaMrxvwnJwcTXH5uoG/7LLLnJUnT2eeeSaLFi3y+VynTp34+eefycvLIy0trdoSTSGEqK1OFIY+5DvS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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:38:02.924638Z",
     "iopub.status.busy": "2025-01-22T14:38:02.924252Z",
     "iopub.status.idle": "2025-01-22T14:38:04.800491Z",
     "shell.execute_reply": "2025-01-22T14:38:04.799864Z",
     "shell.execute_reply.started": "2025-01-22T14:38:02.924602Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_3142/4227932207.py:3: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(\"checkpoints/cifar-10/best.ckpt\", map_location=\"cpu\"))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.4261\n",
      "accuracy: 0.8752\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/cifar-10/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:38:26.940412Z",
     "iopub.status.busy": "2025-01-22T14:38:26.940056Z",
     "iopub.status.idle": "2025-01-22T14:40:22.030575Z",
     "shell.execute_reply": "2025-01-22T14:40:22.029577Z",
     "shell.execute_reply.started": "2025-01-22T14:38:26.940387Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 4688/4688 [01:55<00:00, 40.74it/s]\n"
     ]
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = [] # 预测结果收集器\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data) #得到预测结果\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()] # 得到预测类别，idx_to_label是id到字符串类别的映射\n",
    "    preds_collect.extend(preds)\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:40:26.254694Z",
     "iopub.status.busy": "2025-01-22T14:40:26.254312Z",
     "iopub.status.idle": "2025-01-22T14:40:26.263134Z",
     "shell.execute_reply": "2025-01-22T14:40:26.262563Z",
     "shell.execute_reply.started": "2025-01-22T14:40:26.254668Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>competitions/cifar-10/test/1.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>competitions/cifar-10/test/2.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>competitions/cifar-10/test/3.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>competitions/cifar-10/test/4.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>competitions/cifar-10/test/5.png</td>\n",
       "      <td>cat</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           filepath class\n",
       "0  competitions/cifar-10/test/1.png   cat\n",
       "1  competitions/cifar-10/test/2.png   cat\n",
       "2  competitions/cifar-10/test/3.png   cat\n",
       "3  competitions/cifar-10/test/4.png   cat\n",
       "4  competitions/cifar-10/test/5.png   cat"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:40:40.263689Z",
     "iopub.status.busy": "2025-01-22T14:40:40.263110Z",
     "iopub.status.idle": "2025-01-22T14:40:40.345537Z",
     "shell.execute_reply": "2025-01-22T14:40:40.345056Z",
     "shell.execute_reply.started": "2025-01-22T14:40:40.263664Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>id</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299995</th>\n",
       "      <td>299996</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299996</th>\n",
       "      <td>299997</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299997</th>\n",
       "      <td>299998</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299998</th>\n",
       "      <td>299999</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>299999</th>\n",
       "      <td>300000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>300000 rows × 1 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "            id\n",
       "0            1\n",
       "1            2\n",
       "2            3\n",
       "3            4\n",
       "4            5\n",
       "...        ...\n",
       "299995  299996\n",
       "299996  299997\n",
       "299997  299998\n",
       "299998  299999\n",
       "299999  300000\n",
       "\n",
       "[300000 rows x 1 columns]"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df1 = pd.DataFrame(list(range(1,3*10**5+1)), columns=[\"id\"])\n",
    "test_df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:40:53.300905Z",
     "iopub.status.busy": "2025-01-22T14:40:53.300552Z",
     "iopub.status.idle": "2025-01-22T14:40:53.314746Z",
     "shell.execute_reply": "2025-01-22T14:40:53.314103Z",
     "shell.execute_reply.started": "2025-01-22T14:40:53.300881Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>id</th>\n",
       "      <th>label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>automobile</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   id       label\n",
       "0   1    airplane\n",
       "1   2    airplane\n",
       "2   3  automobile\n",
       "3   4        ship\n",
       "4   5    airplane"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_df1[\"label\"] = preds_collect # 增加预测类别列,比赛要求这一列是label\n",
    "test_df1.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:41:09.984665Z",
     "iopub.status.busy": "2025-01-22T14:41:09.984291Z",
     "iopub.status.idle": "2025-01-22T14:41:10.159909Z",
     "shell.execute_reply": "2025-01-22T14:41:10.159309Z",
     "shell.execute_reply.started": "2025-01-22T14:41:09.984639Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df1.to_csv(\"submission.csv\", index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-22T14:41:14.809894Z",
     "iopub.status.busy": "2025-01-22T14:41:14.809286Z",
     "iopub.status.idle": "2025-01-22T14:41:14.941747Z",
     "shell.execute_reply": "2025-01-22T14:41:14.941018Z",
     "shell.execute_reply.started": "2025-01-22T14:41:14.809870Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cifar-10  monkeys-cnn-relu  monkeys-resnet50\n"
     ]
    }
   ],
   "source": [
    "!ls checkpoints/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-22T14:43:17.299609Z",
     "iopub.status.busy": "2025-01-22T14:43:17.299146Z",
     "iopub.status.idle": "2025-01-22T14:44:41.815300Z",
     "shell.execute_reply": "2025-01-22T14:44:41.814611Z",
     "shell.execute_reply.started": "2025-01-22T14:43:17.299572Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  0%|                                               | 0.00/3.51M [00:00<?, ?B/s]^C\n",
      "  0%|                                               | 0.00/3.51M [01:23<?, ?B/s]\n",
      "User cancelled operation\n"
     ]
    }
   ],
   "source": [
    "!kaggle competitions submit -c cifar-10 -f submission.csv -m \"Message\""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
